#pragma once

#include <cstdint>

#define dlist_for_each_safe(node, temp) \
    for (node = front(), temp = next(node); (node); node = temp, temp = next(node))

namespace coroutine {

template <typename T>
struct _dlist_node_t {
    T data;
private:
    struct _dlist_node_t* prev {nullptr};
    struct _dlist_node_t* next {nullptr};
    template <typename F>
    friend struct _dlist_t;
};

template <typename T>
struct _dlist_t {
    class iterator {
        _dlist_node_t<T>* cur_ {nullptr};
        _dlist_t<T>* list_ {nullptr};

    public:
        iterator(_dlist_t<T>* list = nullptr) {
            list_ = list;
            if (list) {
                cur_ = list->front();
            }
        }

        bool operator!=(const iterator& src) {
            return (cur_ != src.cur_);
        }

        void operator++() {
            if (!list_) {
                return;
            }
            if (cur_) {
                cur_ = list_->next(cur_);
            } else {
                cur_ = list_->front();
            }
        }

        T operator*() {
            return cur_->data;
        }

        const T operator*() const {
            return cur_->data;
        }
    };

    iterator begin() {
        return iterator(this);
    }

    iterator end() {
        return iterator();
    }

private:
    _dlist_node_t<T>* head {nullptr};
    int count {int(0)};

    _dlist_node_t<T>* dlist_node_create() {
        _dlist_node_t<T>* node = new _dlist_node_t<T>();
        node->next = nullptr;
        node->prev = nullptr;
        return node;
    }

    void dlist_node_destroy(_dlist_node_t<T>* node) {
        delete node;
    }

    _dlist_node_t<T>* dlist_node_set_data(_dlist_node_t<T>* node, T data) {
        node->data = data;
        return node;
    }

    uint64_t dlist_node_get_data(_dlist_node_t<T>* node) {
        return node->data;
    }

public:
    _dlist_t() {
        head = dlist_node_create();
        head->next = head;
        head->prev = head;
        count      = 0;
    }

    ~_dlist_t() {
        clear();
        dlist_node_destroy(head);
    }

    void clear() {
        _dlist_node_t<T>* node = nullptr;
        _dlist_node_t<T>* temp = nullptr;
        dlist_for_each_safe(node, temp) {
            delete_node(node);
        }
        head->next = head;
        head->prev = head;
        count = 0;
    }

    void push_front(_dlist_node_t<T>* node) {
        head->next->prev = node;
        node->prev       = head;
        node->next       = head->next;
        head->next       = node;
        count += 1;
    }

    void push_back(_dlist_node_t<T>* node) {
        head->prev->next = node;
        node->next       = head;
        node->prev       = head->prev;
        head->prev       = node;
        count += 1;
    }

    _dlist_node_t<T>* add_front_node(T data) {
        _dlist_node_t<T>* node = nullptr;
        node = dlist_node_create();
        if (!node) {
            return nullptr;
        }
        dlist_node_set_data(node, data);
        push_front(node);
        return node;
    }

    _dlist_node_t<T>* add_tail_node(T data) {
        _dlist_node_t<T>* node = nullptr;
        node = dlist_node_create();
        if (!node) {
            return nullptr;
        }
        dlist_node_set_data(node, data);
        push_back(node);
        return node;
    }

    int size() {
        return count;
    }

    bool empty() {
        return (count == 0);
    }

    _dlist_node_t<T>* remove(_dlist_node_t<T>* node) {
        if (!node->prev || !node->next) {
            return 0;
        }
        node->prev->next = node->next;
        node->next->prev = node->prev;
        node->next = nullptr;
        node->prev = nullptr;
        count -= 1;
        return node;
    }

    void delete_node(_dlist_node_t<T>* node) {
        dlist_node_destroy(remove(node));
    }

    _dlist_node_t<T>* next(_dlist_node_t<T>* node) {
        if (!node) {
            return nullptr;
        }
        if (head == node->next) {
            return nullptr;
        }
        return node->next;
    }

    _dlist_node_t<T>* front() {
        if (head->next == head) {
            return nullptr;
        }
        return head->next;
    }

    _dlist_node_t<T>* back() {
        if (head->prev == head) {
            return nullptr;
        }
        return head->prev;
    }
};

}
